package com.zhl.study.service;

import com.zhl.study.module.Request;
import com.zhl.study.module.Response;
import com.zhl.study.module.ServiceDescriptor;
import lombok.extern.slf4j.Slf4j;
import org.apache.commons.io.IOUtils;

import java.io.ByteArrayInputStream;
import java.io.IOException;
import java.io.InputStream;
import java.lang.reflect.InvocationHandler;
import java.lang.reflect.Method;


/**
 * @description 调用远程服务的代理类
 * @Author: z156940l@outlook.com
 * @className RemoteInvoker
 * @Date: 2024/5/20 10:40
 */
@Slf4j
public class RemoteInvoker implements InvocationHandler {
    private Encoder encoder;
    private Class clazz;
    private Decoder decoder;
    private TransportSelector selector;
    RemoteInvoker(Class clazz,
                  Encoder encoder,
                  Decoder decoder,
                  TransportSelector selector){
        this.encoder = encoder;
        this.decoder = decoder;
        this.clazz = clazz;
        this.selector = selector;
    }
    @Override
    /**
     * 代理类
     * proxy:
     * method:想调用方法
     * args: 方法的参数
     * 返回：方法的返回参数
     */
    public Object invoke(Object proxy,
                         Method method,
                         Object[] args) throws Throwable {
        Request request = new Request();
        request.setService(ServiceDescriptor.from(clazz,method));
        request.setParameters(args);
        Response resp = invokeRemote(request);
        if(resp == null || resp.getCode()!=0){
            throw new IllegalStateException("fail to invoje remote: "+resp.getMessage());
        }
        return resp.getData();
    }

    /**
     * 远程执行，被invoke调用
     * @param request client请求
     * @return 返回 Response类
     */
    private Response invokeRemote(Request request) {
        TransportClient client = null;
        Response resp = null;
        try{
            client = selector.select();

            byte[] outbytes = encoder.encode(request);
            InputStream revice= client.write(new ByteArrayInputStream(outbytes));

            byte[] inBytes = IOUtils.readFully(revice,revice.available());

            resp= decoder.decode(inBytes,Response.class);
        } catch (IOException e) {
            log.warn(e.getMessage(),e);
            resp = new Response();
            resp.setCode(1);
            resp.setMessage("RpcClient got error: "
                    + e.getClass()
                    +" : "+e.getMessage()
            );
        } finally {
            if(client!=null){
                selector.release(client);
            }
        }
        return  resp;
    }
}
